package bsr.basara.autodata.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.jfinal.plugin.activerecord.Record;
import org.nfunk.jep.JEP;

import java.math.RoundingMode;
import java.util.*;

/**
 * 数据生成工具类
 *
 * @author basara
 */
public class DataUtils {

    /**
     * 将record的key全部转成小写
     *
     * @param records
     * @return
     */
    public static List<Record> toLowerCase(List<Record> records) {
        List<Record> recordList = new ArrayList<>();
        for (Record record : records) {
            Record newRecord = new Record();
            Map<String, Object> columns = record.getColumns();
            for (String key : columns.keySet()) {
                String newKey = key.toLowerCase();
                newRecord.set(newKey, columns.get(key));
            }
            recordList.add(newRecord);
        }
        return recordList;
    }

    /**
     * 在某个范围波动
     *
     * @param upper 上限
     * @param lower 下限
     * @return
     */
    public static Double randomByLimit(Double upper, Double lower) {
        double value = 0.0;
        if (upper != null && lower != null) {
            value = RandomUtil.randomDouble(lower, upper, 4, RoundingMode.HALF_UP);
        }
        return value;
    }

    /**
     * 在某个基数上波动
     *
     * @param base  基数
     * @param ratio 波动率
     * @return
     */
    public static Double randomByBase(Double base, Double ratio) {
        double value = 0.0;
        if (base != null && ratio != null) {
            value = RandomUtil.randomDouble(base - base * ratio, base + base * ratio, 4, RoundingMode.HALF_UP);
        }
        return value;
    }

    /**
     * 公式计算
     *
     * @param formula 公式
     * @param his     已有数据
     * @return
     */
    public static Double formula(String formula, Record his) {
        double value = 0.0;
        if (StrUtil.isNotEmpty(formula)) {
            JEP jep = new JEP();
            //添加常用函数
            jep.addStandardFunctions();
            //添加常用常量
            jep.addStandardConstants();
            if (his != null) {
                Map<String, Object> columns = his.getColumns();
                for (String key : columns.keySet()) {
                    String newKey = key.toLowerCase();
                    jep.addVariable(newKey, columns.get(key));
                }
            }
            if (formula.contains("{{r}}")) {
                formula = formula.replace("{{r}}", RandomUtil.randomDouble() + "");
            }
            jep.parseExpression(formula);
            value = jep.getValue();
        }
        return value;
    }

    /**
     * 构造基于圆圈曲线数据集合
     * <p>
     * to必须大于from且2者必须在x轴同一侧
     * 当from在[0,180)时to为(from,180]
     * 当from在[180,360)时to为(from,360]
     *
     * @param size 集合大小
     * @param from 度数从
     * @param to   读书到
     */
    public static List<Double> referCircle(int size, Integer from, Integer to) {
        if (from == null || to == null || from < 0 || to > 360 || from > to) {
            return null;
        }
        List<Double> s = new ArrayList<>();
        //360度cos值数组
        double[] cos = new double[361];
        for (int i = 0; i < 361; i++) {
            cos[i] = Math.cos(Math.toRadians(i));
        }
        //x最小值
        double xFrom;
        //x最大值
        double xTo;
        //曲线在x轴上部
        if (to <= 180) {
            xFrom = cos[to];
            xTo = cos[from];
        } else {
            //曲线在x轴下部
            xFrom = cos[from];
            xTo = cos[to];
        }
        //均匀切分x得到步长
        double step = (xTo - xFrom) / size;
        double x = xFrom;
        for (int i = 0; i < size; i++) {
            //利用勾股定理获取y值
            double y = Math.sqrt(1 - x * x);
            if (to <= 180) {
                s.add(y);
            } else {
                s.add(-y);
            }
            x += step;
        }
        return s;
    }

    /**
     * 构造基于正弦曲线数据集合
     *
     * @param size 集合大小
     * @param sine 趋势配置
     */
    public static List<Double> referSine(int size, String sine) {
        List<Double> s = new ArrayList<>();
        List<Double> s1 = new ArrayList<>();
        List<Double> s2 = new ArrayList<>();
        List<Double> s3 = new ArrayList<>();
        List<Double> s4 = new ArrayList<>();
        double[] sin = new double[360];
        for (int i = 0; i < 360; i++) {
            sin[i] = Math.sin(Math.toRadians(i));
            if (i < 90) {
                s2.add(sin[i]);
            } else if (i < 180) {
                s3.add(sin[i]);
            } else if (i < 270) {
                s4.add(sin[i]);
            } else {
                s1.add(sin[i]);
            }
        }
        for (char v : sine.toCharArray()) {
            if (v == '1') {
                s.addAll(s1);
            }
            if (v == '2') {
                s.addAll(s2);
            }
            if (v == '3') {
                s.addAll(s3);
            }
            if (v == '4') {
                s.addAll(s4);
            }
        }
        List<Double> list = new ArrayList<>();
        int sizes = s.size();
        for (int i = 0; i < size; i++) {
            int i1 = sizes * i / size;
            list.add(s.get(i1));
        }
        return list;
    }


    /**
     * 生成趋势数据
     *
     * @param referData 参考曲线数据集
     * @param noise     噪音 值越小曲线越平滑 必须大于0 默认0.01
     * @param max       最高点估值
     * @param min       最低点估值
     * @param ii        数据位置
     * @return
     */
    public static Double createData(List<Double> referData, Double noise, Double max, Double min, int ii) {
        if (referData == null || referData.size() < 1) {
            return null;
        }
        DoubleSummaryStatistics statistics = referData.stream().mapToDouble(Double::doubleValue).summaryStatistics();
        double smax = statistics.getMax();
        double smin = statistics.getMin();

        noise = noise == null ? 0.01 : noise;
        max = max == null ? 100 : max;
        min = min == null ? 0 : min;

        double value = RandomUtil.randomDouble(referData.get(ii) - noise, referData.get(ii) + noise, 6, RoundingMode.HALF_UP);
        //归限值化 (x-min)/(max-min)*放大+偏移
        value = (value - smin) / (smax - smin) * (max - min) + min;

        return value;
    }

    /**
     * 获取给定时间最近的整xx分钟
     *
     * @param now       当前时间
     * @param timeSpace 数据间隔
     * @return
     */
    public static Date hisTime(Date now, int timeSpace) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(DateUtil.parse(DateUtil.formatDateTime(now)));

        int minute = calendar.get(Calendar.MINUTE);
        calendar.add(Calendar.MINUTE, -minute % timeSpace);
        calendar.set(Calendar.SECOND, 0);

        return calendar.getTime();
    }

    public static void main(String[] args) {

        int n = 96;
//        List<Double> s = new ArrayList<>();
//        for (int i = 0; i < n; i++) {
//            double x = (double) i / n;
//            Double y = TrendUtils.sinusoidal(x, 1);
//            s.add(y);
//        }

        List<Double> s = referSine(n, "1234");

        for (int i = 0; i < n; i++) {
            Double value = createData(s, 0.01, 100.0, 30.0, i);
            System.out.println(value);
        }


    }


}
